
\chapter{Tenon执行器指令集规范}

在Tenon执行器初始化完成并加载Tenon中间码之后，会查找入口函数并执行其编译成的指令流，
指令流中的每条指令都为32位(4字节)长度。Tenon执行器使用了Tenon自定义的指令集。

\section{编码格式和一般约定}
Tenon指令集中的每种指令都使用了定长的32位长度。其格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{20em}|>{\centering\arraybackslash}p{6em}|}
\hline
bit 31 .. bit 6 & bit 5 .. bit 0\\
\hline
 操作数 & 操作码\\
\hline
\end{tabular}

\vspace{2ex}

每个Tenon指令的低6位为该指令的操作码，高26位为该指令所包含的操作数，
一条指令可能包含0个、1个、2个或3个操作数。
操作数分为6种：\verb!函数操作数!、\verb!立即数操作数!、\verb!寄存器操作数!、\verb!全局变量操作数!、
\verb!局部变量操作数!、\verb!字面量操作数!。

函数操作数：占据15位的长度，其值代表了对应的函数在函数表中的索引。

立即数操作数：立即数的值存在于指令中，根据不同的指令，可能占据8位、20位的长度。

寄存器操作数：占据5位的长度，其值代表了所使用的寄存器的编号，从R1 至 R31，R0作为保留值。

全局变量操作数：占据15位的长度，其值代表了对应的全局变量在全局变量表中的索引。

局部变量操作数：占据15位的长度，其值代表了对应的局部变量在局部变量表中的索引。

字面量操作数：占据15位的长度，其值代表了对应的字面量在字面量表中的索引。

有时，一条指令中的操作数可以是上面所说的操作数中的几种，在这种情况下，
我们在指令中用三位长度的标志flag对其进行区分：

\begin{itemize}
    \item flag为0，代表寄存器操作数
    \item flag为1，代表局部变量操作数
	\item flag为2，代表全局变量操作数
	\item flag为3，代表字面量操作数
    \item flag为4，代表立即数操作数
    \item flag为5，代表函数操作数
\end{itemize}

\section{load指令}

load指令用于将局部变量或全局变量的值加载到寄存器中，以进行运算。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{6em}|}
\hline
bit 28 .. bit 26 & bit 25 .. bit 11 & bit 10 .. bit 6 & bit 5 .. bit 0\\
\hline
 flag & index & register & OPC\_LOAD\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_LOAD，位6至位10这5位是一个寄存器操作数，是load指令的目的操作数；
位11至位25这15位可以是寄存器操作数、局部变量操作数或全局变量操作数，
通过位26至位28的标志进行区分，是load指令的源操作数。
位29至位31无效。

当load指令的源操作数为局部变量操作数或全局变量操作数时，加载的是对应的局部变量或全局变量的值；
当load指令的源操作数为寄存器变量时，需要从寄存器中保存的局部变量或全局变量的地址处加载变量的值（寄存器间接寻址）。
load指令的源操作数不能是字面量操作数。

需要注意的是，load指令加载的变量只能是内置类型或枚举类型。

\section{store指令}

store指令用于将寄存器中的值存储到局部变量或全局变量中。store指令与load指令类似，不同的是数据传输方向。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 28 .. bit 24 & bit 23 .. bit 21 & bit 20 .. bit 6 & bit 5 .. bit 0\\
\hline
 register & flag & index & OPC\_STORE\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_STORE，位6至位20这15位可以是寄存器操作数、局部变量操作数或全局变量操作数，
通过位21至位23的标志进行区分，是store指令的目的操作数；
位24至位28这5位是一个寄存器操作数，是store指令的源操作数。
位29至位31无效。

当store指令的目的操作数为局部变量操作数或全局变量操作数时，需要将源操作数中的值存储到对应的局部变量或全局变量中；
当store指令的目的操作数为寄存器变量时，需要将源操作数中的值存储到寄存器中保存的局部变量或全局变量的地址处（寄存器间接寻址）。
store指令的目的操作数不能是字面量操作数。

需要注意的是，store指令存储的变量只能是内置类型或枚举类型。

\section{move指令}

move指令用于加载局部变量、全局变量的地址或立即数的值到寄存器中；或者用于在寄存器间传递数据。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{6em}|}
\hline
bit 28 .. bit 26 & bit 25 .. bit 11 & bit 10 .. bit 6 & bit 5 .. bit 0\\
\hline
 flag & index & register & OPC\_MOVE\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_MOVE，位6至位10这5位是一个寄存器操作数，是move指令的目的操作数；
位11至位25这15位可以是寄存器操作数、字面量操作数、局部变量操作数或全局变量操作数，
通过位26至位28的标志进行区分，是move指令的源操作数。
位29至位31无效。

\section{getptr指令}

getptr指令用于获取数组元素或记录成员的地址。其编码格式如下：

\vspace{2ex}

\noindent \begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 28 .. bit 24 & bit 23 .. bit 21 & bit 20 .. bit 6 & bit 5 .. bit 0\\
\hline
 register & flag & index & OPC\_GETPTR\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_GETPTR。位6至位20可以是寄存器操作数、局部变量操作数或全局变量操作数，
通过位21至位23的标志进行区分。当其为寄存器操作数时，寄存器中存放的即是基地址；
当其为局部变量操作数或全局变量操作数时，操作数值即基地址。

位24至位28这5位是一个寄存器操作数，既用作获取数组元素时的偏移量（数值）或获取记录成员时的成员名（字符串），
也用作获取的地址结果的保存。
位29至位31无效。


\section{寄存器运算指令}

寄存器运算指令用于进行算术运算、逻辑运算或比较运算。寄存器运算指令中的操作数均为寄存器操作数。这些指令包括：
\begin{itemize}
    \item 算术运算指令, 包括add、sub、mul、div、idiv、imod
    \item 逻辑运算指令，包括xor、not、uminus
	\item 比较运算指令，包括le、lt、ge、gt、eq、ne
\end{itemize}

上述指令中，not指令和uminus指令有两个操作数，除此之外，均有三个操作数。

not指令和uminus指令的编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{6em}|}
\hline
bit 31 .. bit 16 & bit 15 .. bit 11 & bit 10 .. bit 6 & bit 5 .. bit 0\\
\hline
 - & dst & src & opc\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码，位6至位10这5位为指令的源操作数，位11至位15这5位为指令的目的操作数。

其它寄存器运算指令的编码格式如下：

\vspace{2ex}

\noindent \begin{tabular}{|>{\centering\arraybackslash}p{3em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{6em}|}
\hline
 - & bit 20 .. bit 16 & bit 15 .. bit 11 & bit 10 .. bit 6 & bit 5 .. bit 0\\
\hline
- & dst & src2 & src1 & opc\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码，位6至位10、位11至位15分别为指令的一个源操作数，位16至位20为指令的目的操作数。位21至位31无效。

\section{return指令}

return指令用于在程序中退出函数的执行。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{16em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 31 .. bit 6 & bit 5 .. bit 0\\
\hline
 - & OPC\_RETURN\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_RETURN，位6至位31无效。

\section{brf指令}

brf(break if false)指令用于判断条件为假时，在程序中进行短跳转。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{12em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{6em}|}
\hline
bit 30 .. bit 11 & bit 10 .. bit 6 & bit 5 .. bit 0\\
\hline
 offset & register & OPC\_BRF\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_BRF，位6至位10是寄存器操作数，是用于brf指令的判断条件。
位11至位30是立即数操作数，执行brf指令跳转时，跳转目标相对当前pc值的偏移量。
位31无效。

\section{brt指令}

brt(break if true)指令用于判断条件为真时，在程序中进行短跳转。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{12em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{6em}|}
\hline
bit 30 .. bit 11 & bit 10 .. bit 6 & bit 5 .. bit 0\\
\hline
 offset & register & OPC\_BRT\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_BRT，位6至位10是寄存器操作数，是用于brt指令的判断条件。
位11至位30是立即数操作数，执行brt指令跳转时，跳转目标相对当前pc值的偏移量。
位31无效。

\section{jmp指令}

jmp指令用于在程序中进行长跳转。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{18em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 25 .. bit 6 & bit 5 .. bit 0\\
\hline
 offset & OPC\_JMP\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_JMP，位6至位25是立即数操作数，是执行jmp指令跳转时，跳转目标相对当前pc值的偏移量。

\section{pusharg指令}

pusharg指令用于在函数调用时，将函数参数压入参数栈中。其编码格式如下：

\vspace{2ex}

\noindent \begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 31 .. bit 24 & bit 23 .. bit 21 & bit 20 .. bit 6 & bit 5 .. bit 0\\
\hline
 arg\_flag & flag & index & OPC\_PUSHARG\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_PUSHARG，位6至位20这15位可以是字面量操作数、寄存器操作数、局部变量操作数或全局变量操作数，
通过位21至位23的标志进行区分，是pusharg指令的操作数；
位24至位31这8位是操作数的标志。

当pusharg指令的操作数为字面量操作数、局部变量操作数或全局变量操作数时，将操作数的地址压入参数栈中；
当pusharg指令的操作数为寄存器操作数时，将寄存器中保存的值压入参数栈中。

\section{pushret指令}

pushret指令用于在函数执行结束时，将函数的返回值压入返回值栈中。其编码格式如下：

\vspace{2ex}

\noindent \begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 31 .. bit 24 & bit 23 .. bit 21 & bit 20 .. bit 6 & bit 5 .. bit 0\\
\hline
 - & flag & index & OPC\_PUSHRET\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_PUSHRET，位6至位20这15位可以是字面量操作数、寄存器操作数、局部变量操作数或全局变量操作数，
通过位21至位23的标志进行区分，是pushret指令的操作数；
位24至位31这8位无效。

当pushret指令的操作数为字面量操作数、局部变量操作数或全局变量操作数时，将操作数的地址压入返回值栈中；
当pushret指令的操作数为寄存器操作数时，将寄存器中保存的值压入返回值栈中。

\section{popret指令}

popret指令用于在函数执行后，从返回值栈中弹出函数的返回值。其编码格式如下：

\vspace{2ex}

\noindent \begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 31 .. bit 24 & bit 23 .. bit 21 & bit 20 .. bit 6 & bit 5 .. bit 0\\
\hline
 - & flag & index & OPC\_POPRET\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_POPRET，位6至位20这15位可以是寄存器操作数、局部变量操作数或全局变量操作数，
通过位21至位23的标志进行区分，位24至位31这8位无效。

执行popret指令时，将返回值栈中弹出的值赋值给相应的寄存器、局部变量或全局变量。

\section{call指令}

call指令用于调用相应的函数。其编码格式如下：

\vspace{2ex}

\noindent \begin{tabular}{|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{7em}|>{\centering\arraybackslash}p{6em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 31 .. bit 24 & bit 23 .. bit 21 & bit 20 .. bit 6 & bit 5 .. bit 0\\
\hline
 argc & flag & index & OPC\_CALL\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_CALL。位6至位20这15位可以是函数操作数或寄存器操作数，
通过位21至位23的标志进行区分；位24至位31这8位是立即数操作数。

\section{ibreak指令}

ibreak指令用于调试Tenon程序时的中断。其编码格式如下：

\vspace{2ex}

\begin{tabular}{|>{\centering\arraybackslash}p{16em}|>{\centering\arraybackslash}p{8em}|}
\hline
bit 31 .. bit 6 & bit 5 .. bit 0\\
\hline
 - & OPC\_IBREAK\\
\hline
\end{tabular}

\vspace{2ex}

其中，低6位为指令码OPC\_IBREAK，位6至位31无效。
